package com.md.api.service.impl;

import cn.hutool.core.date.DateUtil;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.service.WxPayService;
import com.md.api.common.exception.BusinessException;
import com.md.api.common.utils.*;
import com.md.api.dao.*;
import com.md.api.entity.*;
import com.md.api.entity.enums.*;
import com.md.api.service.RechargeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;

/**
 * @Author liujie
 * @ClassName ProductServiceImpl
 **/

@Slf4j
@Service
public class RechargeServiceImpl implements RechargeService {
    @Resource
    TokenUtil tokenUtil;
    @Resource
    UserBalanceRecordMapper balanceRecordMapper;
    @Resource
    UserMapper userMapper;
    @Resource
    RechargeOrderMapper orderMapper;
    @Value("${wx.pay.rechargeNotifyUrl}")
    private String rechargeNotifyUrl;
    @Autowired
    private WxPayService wxService;
    @Resource
    UserPointRecordMapper pointsRecordMapper;


    @Override
    public HttpResult recharge(HttpServletRequest request, RechargeOrder order) {
        try {
            String userId = tokenUtil.getUserId(request);
            //创建订单号
            String code = CodeUtil.verifyCode(6);
            String today = DateUtil.format(new Date(), "yyyyMMdd");
            String orderNo = "sn".concat(today).concat(code);
            order.setPayStatus(PayStatus.PAYING.value());
            order.setOrderNo(orderNo);
            order.setUserId(userId);
            orderMapper.initOrder(order);

            // TODO 调用统一生成订单接口
            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            orderRequest.setSignType(WxPayConstants.SignType.MD5);
            orderRequest.setBody("余额充值");
            orderRequest.setOutTradeNo(orderNo); //自己生成order_No
            orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
            orderRequest.setTotalFee(BaseWxPayRequest.yuanToFen(order.getActualPay().toString()));//直接分
            orderRequest.setOpenid(userId); // 获取微信支付用户的openId
            orderRequest.setSpbillCreateIp(IpUtil.getRealIp(request));
            Date now = new Date();
            Date afterDate = DateUtils.add(now, Calendar.MINUTE, 10);//10分钟后
            orderRequest.setTimeStart(DateUtils.format(now, DateUtils.yyyyMMddHHmmssFormat));
            orderRequest.setTimeExpire(DateUtils.format(afterDate, DateUtils.yyyyMMddHHmmssFormat));
            orderRequest.setNotifyUrl(rechargeNotifyUrl);
            WxPayMpOrderResult wxPayMpOrderResult = wxService.createOrder(orderRequest);
            WxOrderResult wxOrder = new WxOrderResult();
            wxOrder.setWxPayMpOrderResult(wxPayMpOrderResult);
            wxOrder.setOrderNo(orderNo);
            return new HttpResult(wxOrder);
        }catch (Exception e){
            e.printStackTrace();
            return HttpResult.fail("初始化充值失败！");
        }
    }
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    @Override
    public String rechargeNotify(HttpServletRequest request) {
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            WxPayOrderNotifyResult notifyResult = wxService.parseOrderNotifyResult(xmlResult);
            String orderNo = notifyResult.getOutTradeNo();
            String tradeNo = notifyResult.getTransactionId();
            String totalFee = BaseWxPayResult.fenToYuan(notifyResult.getTotalFee());

            RechargeOrder dbOrder = orderMapper.getByOrderNo(orderNo);
            if(dbOrder.getPayStatus()!=PayStatus.PAYING.value()){
                log.info("订单号："+orderNo+"，微信支付重复回调");
                return WxPayNotifyResponse.success("success");
            }
            dbOrder.setPayTime(new Date());
            dbOrder.setCloseTime(new Date());
            dbOrder.setEndTime(new Date());
            if(!"SUCCESS".equals(notifyResult.getResultCode())) {
                dbOrder.setPayStatus(PayStatus.FAIL.value());
//                dbOrder.setFailReason(notifyResult.get());
                orderMapper.updateOrder(dbOrder);
                return WxPayNotifyResponse.success("success");
            }
            if (dbOrder.getActualPay()>Double.valueOf(totalFee)){
                dbOrder.setPayStatus(PayStatus.FAIL.value());
                dbOrder.setFailReason("订单金额应该与返回金额不一致");
                orderMapper.updateOrder(dbOrder);
                return WxPayNotifyResponse.fail("订单金额应该与返回金额不一致");
            }
            UserInfo user = userMapper.getByUserId(dbOrder.getUserId());
            user.setBalance(BigDecimalUtil.add(user.getBalance(),dbOrder.getActualPay()).doubleValue());
            userMapper.updateBalance(user);
            user.setAccrueExpend(BigDecimalUtil.add(user.getAccrueExpend(),dbOrder.getActualPay()).doubleValue());
            userMapper.updateAccrueExpend(user);

            UserBalanceRecord balanceRecord = new UserBalanceRecord();
            balanceRecord.setBalance(dbOrder.getActualPay());
            balanceRecord.setUserId(user.getUserId());
            balanceRecord.setRecordName("余额充值");
            balanceRecord.setRecordType(1);
            balanceRecord.setOrderNo(orderNo);
            balanceRecordMapper.insert(balanceRecord);
            dbOrder.setPayStatus(PayStatus.SUCCESS.value());
            orderMapper.updateOrder(dbOrder);
            return WxPayNotifyResponse.success("success");
        }catch (Exception e){
            e.printStackTrace();
            throw new BusinessException("充值回调失败"+e.getMessage());
        }
    }


    @Override
    public void successPay(String orderNo, Integer totalFee,String tradeState) {
        try {
            RechargeOrder dbOrder = orderMapper.getByOrderNo(orderNo);
            if(!"SUCCESS".equals(tradeState)) {
                dbOrder.setPayStatus(PayStatus.FAIL.value());
                orderMapper.updateOrder(dbOrder);
                return;
            }
            dbOrder.setPayTime(new Date());
            dbOrder.setCloseTime(new Date());
            dbOrder.setEndTime(new Date());
            Integer actualPay = BaseWxPayRequest.yuanToFen(dbOrder.getActualPay().toString());
            if (totalFee<actualPay){
                dbOrder.setPayStatus(PayStatus.FAIL.value());
                dbOrder.setFailReason("订单金额应该与返回金额不一致");
                orderMapper.updateOrder(dbOrder);
                return;
            }
            UserInfo user = userMapper.getByUserId(dbOrder.getUserId());
            user.setBalance(BigDecimalUtil.add(user.getBalance(),dbOrder.getActualPay()).doubleValue());
            userMapper.updateBalance(user);
            user.setAccrueExpend(BigDecimalUtil.add(user.getAccrueExpend(),dbOrder.getActualPay()).doubleValue());
            userMapper.updateAccrueExpend(user);

            UserBalanceRecord balanceRecord = new UserBalanceRecord();
            balanceRecord.setBalance(dbOrder.getActualPay());
            balanceRecord.setUserId(user.getUserId());
            balanceRecord.setRecordName("余额充值");
            balanceRecord.setRecordType(1);
            balanceRecord.setOrderNo(orderNo);
            balanceRecordMapper.insert(balanceRecord);
            dbOrder.setPayStatus(PayStatus.SUCCESS.value());
            orderMapper.updateOrder(dbOrder);
        }catch (Exception e){
            e.printStackTrace();
            throw new BusinessException("充值回调失败"+e.getMessage());
        }
    }
}
